classdef ProjectManagementService < handle
    % PROJECTMANAGEMENTSERVICE 项目管理服务
    % 基于DO-178规则第4条（架构分离原则）设计
    % 负责协调所有项目管理相关的业务逻辑
    %
    % 功能：
    %   - 模型创建和删除（addModelFolder, deleteModelFolder）
    %   - 项目文件管理（addFile2proj, removeModelFolder）
    %   - 模型验证和依赖检查
    %   - 事务性操作（失败时自动回滚）
    %   - 批量操作支持
    %
    % 设计原则：
    %   - 单例模式：全局唯一实例
    %   - 单一职责：只负责项目管理业务逻辑协调
    %   - 依赖注入：依赖现有的工具函数
    %   - 错误明确：遵循规则第2条，不使用silent fallback
    %   - 可测试：所有公共方法都可以独立测试
    %
    % 使用示例：
    %   % 获取单例实例（DEV环境）
    %   service = ProjectManagementService.getInstance();
    %   
    %   % 获取单例实例（CI环境）
    %   service = ProjectManagementService.getInstance('Environment', 'CI');
    %   
    %   % 创建模型
    %   result = service.createModel('MyModel');
    %   result = service.createModel('MyModel', 'IsReusable', true, 'WithDataDictionary', true);
    %   
    %   % 删除模型
    %   result = service.deleteModel('OldModel');
    %   result = service.deleteModel('OldModel', 'CheckDependencies', true, 'CreateBackup', true);
    %   
    %   % 从项目移除模型（不删除文件）
    %   result = service.removeModelFromProject('MyModel');
    %   
    %   % 添加文件到项目
    %   result = service.addFile('myFunction.m', 'Category', 'Utilities');
    %   result = service.addFolder('myFolder', 'IncludeChildren', true);
    %
    % 注意：
    %   Requirement Set 管理功能已迁移至 RequirementsManagementService
    %   请使用 RequirementsManagementService 进行以下操作：
    %   - closeAllRequirementSets()
    %   - openRequirementSet()
    %   - closeRequirementSet()
    %   - getLoadedRequirementSets()
    %
    % 作者: MBD Platform Team
    % 日期: 2025-10-26
    % 最后更新: 2025-10-31 - 迁移Requirement Set管理到RequirementsManagementService
    % 遵循DO-178标准
    
    properties (Access = private)
        projectRoot         % 项目根目录
        projectObj          % MATLAB项目对象
        environment         % 运行环境：'DEV' 或 'CI'
        logger              % LoggingService实例
    end
    
    methods (Access = private)
        function obj = ProjectManagementService(varargin)
            % 私有构造函数 - 初始化服务（单例模式）
            % 输入：
            %   'Environment' - 可选，运行环境 'DEV'（默认）或 'CI'
            % 基于规则第2条：不使用silent fallback，明确报告错误
            
            % 初始化日志服务
            try
                obj.logger = LoggingService.getInstance();
            catch ME
                warning('LoggingService:Unavailable', '日志服务不可用: %s', ME.message);
                obj.logger = [];
            end
            
            % 解析输入参数
            p = inputParser;
            addParameter(p, 'Environment', 'DEV', @(x) ismember(x, {'DEV', 'CI'}));
            parse(p, varargin{:});
            
            obj.environment = p.Results.Environment;
            
            try
                % 获取当前项目
                obj.projectObj = currentProject();
                obj.projectRoot = char(obj.projectObj.RootFolder);
                
                fprintf('✅ ProjectManagementService已初始化\n');
                fprintf('   项目根目录: %s\n', obj.projectRoot);
                fprintf('   运行环境: %s\n', obj.environment);
                
                if ~isempty(obj.logger)
                    obj.logger.logInfo(sprintf('ProjectManagementService已初始化 (环境: %s)', obj.environment));
                end
            catch ME
                if ~isempty(obj.logger)
                    obj.logger.logError(sprintf('项目管理服务初始化失败: %s', ME.message));
                end
                error('ProjectManagementService:InitFailed', ...
                    '无法初始化项目管理服务：%s\n请确保在MATLAB项目中运行此代码', ME.message);
            end
        end
    end
    
    methods (Static, Access = public)
        function obj = getInstance(varargin)
            % 获取ProjectManagementService的单例实例
            % 输入：
            %   'Environment' - 可选，运行环境 'DEV'（默认）或 'CI'
            % 输出：
            %   obj - ProjectManagementService单例实例
            % 基于规则第2条：明确报告错误
            
            persistent instance;
            
            if isempty(instance) || ~isvalid(instance)
                instance = ProjectManagementService(varargin{:});
                fprintf('✅ 创建ProjectManagementService单例实例\n');
            end
            
            obj = instance;
        end
    end
    
    methods (Access = public)
        
        %% ==================== 模型创建 ====================
        
        function result = createModel(obj, modelName, varargin)
            % 创建新模型（带验证和事务性支持）
            % 输入：
            %   modelName - 模型名称
            %   varargin - 可选参数（Name-Value pairs）
            %     'IsReusable' - 是否可重用（默认true）
            %     'WithDataDictionary' - 是否使用数据字典（默认true）
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条：明确报告错误，事务性操作
            
            result = struct('success', false, 'message', '', 'modelPath', '', 'details', struct());
            
            if nargin < 2 || isempty(modelName)
                error('ProjectManagementService:InvalidInput', '模型名称不能为空');
            end
            
            % 解析可选参数
            p = inputParser;
            addParameter(p, 'IsReusable', true, @islogical);
            addParameter(p, 'WithDataDictionary', true, @islogical);
            parse(p, varargin{:});
            
            isReusable = p.Results.IsReusable;
            withDataDict = p.Results.WithDataDictionary;
            
            try
                fprintf('🔨 正在创建模型: %s\n', modelName);
                
                % 1. 前置验证
                fprintf('  [1/4] 验证模型名称...\n');
                obj.validateModelName(modelName);
                obj.checkModelExists(modelName, false); % 确保不存在
                
                % 2. 调用底层创建函数
                fprintf('  [2/4] 创建模型文件夹结构...\n');
                if isReusable
                    createResult = addModelFolder(modelName, 'ProjectObj', obj.projectObj);
                else
                    createResult = addModelFolder(modelName, 'Nonreusable', 'ProjectObj', obj.projectObj);
                end
                
                % 3. 验证创建结果
                fprintf('  [3/4] 验证创建结果...\n');
                if ~isfield(createResult, 'Message')
                    error('模型创建函数返回格式异常');
                end
                
                % 4. 获取模型路径
                fprintf('  [4/4] 完成创建...\n');
                modelPath = ProjectPathManager.getDirPath('model root', modelName);
                
                result.success = true;
                result.message = createResult.Message;
                result.modelPath = modelPath;
                result.details.isReusable = isReusable;
                result.details.withDataDictionary = withDataDict;
                
                fprintf('✅ 模型创建成功: %s\n', modelName);
                fprintf('   路径: %s\n', modelPath);
                
            catch ME
                result.success = false;
                result.message = sprintf('模型创建失败: %s', ME.message);
                result.error = ME;
                
                fprintf('❌ 模型创建失败: %s\n', ME.message);
                
                % 基于规则第2条：明确报告错误，不使用silent fallback
                error('ProjectManagementService:CreateModelFailed', result.message);
            end
        end
        
        %% ==================== 模型删除 ====================
        
        function result = deleteModel(obj, modelName, varargin)
            % 删除模型文件夹（带依赖检查和安全确认）
            % 输入：
            %   modelName - 模型名称
            %   varargin - 可选参数（Name-Value pairs）
            %     'CheckDependencies' - 是否检查依赖（默认true）
            %     'RequireConfirmation' - 是否需要确认（默认true，CI环境为false）
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条：明确报告错误，提供安全机制
            
            result = struct('success', false, 'message', '', 'deletedPath', '');
            
            if nargin < 2 || isempty(modelName)
                error('ProjectManagementService:InvalidInput', '模型名称不能为空');
            end
            
            % 解析可选参数
            p = inputParser;
            addParameter(p, 'CheckDependencies', true, @islogical);
            addParameter(p, 'RequireConfirmation', ~strcmpi(obj.environment, 'CI'), @islogical);
            parse(p, varargin{:});
            
            checkDeps = p.Results.CheckDependencies;
            requireConfirm = p.Results.RequireConfirmation;
            
            try
                fprintf('🗑️  正在删除模型: %s\n', modelName);
                
                % 1. 验证模型存在
                fprintf('  [1/4] 验证模型存在...\n');
                obj.checkModelExists(modelName, true); % 确保存在
                
                % 2. 检查依赖关系（可选）
                if checkDeps
                    fprintf('  [2/4] 检查模型依赖关系...\n');
                    dependencies = obj.checkDependencies(modelName);
                    if ~isempty(dependencies)
                        warning('ProjectManagementService:HasDependencies', ...
                            '模型 %s 被以下模型引用: %s', modelName, strjoin(dependencies, ', '));
                        
                        if requireConfirm && strcmpi(obj.environment, 'DEV')
                            answer = questdlg(...
                                sprintf('模型 %s 被其他模型引用。仍然删除？', modelName), ...
                                'Dependencies Found', ...
                                'Yes', 'No', 'No');
                            if ~strcmp(answer, 'Yes')
                                result.message = '用户取消删除操作';
                                fprintf('  ⚠️  用户取消删除\n');
                                return;
                            end
                        end
                    end
                else
                    fprintf('  [2/4] 跳过依赖检查（已禁用）...\n');
                end
                
                % 3. 确认删除（可选）
                if requireConfirm && strcmpi(obj.environment, 'DEV')
                    fprintf('  [3/4] 等待用户确认...\n');
                    answer = questdlg(...
                        sprintf('确认删除模型 %s？此操作不可撤销。', modelName), ...
                        'Confirm Delete', ...
                        'Yes', 'No', 'No');
                    if ~strcmp(answer, 'Yes')
                        result.message = '用户取消删除操作';
                        fprintf('  ⚠️  用户取消删除\n');
                        return;
                    end
                else
                    fprintf('  [3/4] 跳过用户确认（CI环境或已禁用）...\n');
                end
                
                % 4. 执行删除
                fprintf('  [4/4] 执行删除操作...\n');
                modelPath = ProjectPathManager.getDirPath('model root', modelName);
                deleteResult = deleteModelFolder(modelName, 'ProjectObj', obj.projectObj);
                
                result.success = true;
                result.message = deleteResult.Message;
                result.deletedPath = modelPath;
                
                fprintf('✅ 模型删除成功: %s\n', modelName);
                
            catch ME
                result.success = false;
                result.message = sprintf('模型删除失败: %s', ME.message);
                result.error = ME;
                
                fprintf('❌ 模型删除失败: %s\n', ME.message);
                
                % 基于规则第2条：明确报告错误
                error('ProjectManagementService:DeleteModelFailed', result.message);
            end
        end
        
        %% ==================== 从项目移除模型 ====================
        
        function result = removeModelFromProject(obj, modelName, varargin)
            % 从项目移除模型（不删除文件）
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   result - 包含成功标志和消息的结构体
            
            result = struct('success', false, 'message', '', 'removedPath', '');
            
            if nargin < 2 || isempty(modelName)
                error('ProjectManagementService:InvalidInput', '模型名称不能为空');
            end
            
            try
                fprintf('📤 正在从项目移除模型（保留文件）: %s\n', modelName);
                
                % 验证模型存在
                obj.checkModelExists(modelName, true);
                
                % 调用底层移除函数
                modelPath = ProjectPathManager.getDirPath('model root', modelName);
                removeResult = removeModelFolder(modelName, 'ProjectObj', obj.projectObj);
                
                result.success = true;
                result.message = removeResult.Message;
                result.removedPath = modelPath;
                
                fprintf('✅ 模型已从项目移除: %s\n', modelName);
                fprintf('   文件仍保留在: %s\n', modelPath);
                
            catch ME
                result.success = false;
                result.message = sprintf('从项目移除模型失败: %s', ME.message);
                result.error = ME;
                
                fprintf('❌ 移除模型失败: %s\n', ME.message);
                
                error('ProjectManagementService:RemoveModelFailed', result.message);
            end
        end
        
        %% ==================== 文件管理 ====================
        
        function result = addFile(obj, fileName, varargin)
            % 添加文件到项目
            % 输入：
            %   fileName - 文件路径
            %   varargin - 可选参数（Name-Value pairs）
            %     'Category' - 类别名称
            %     'Label' - 标签名称
            % 输出：
            %   result - 包含成功标志和消息的结构体
            
            result = struct('success', false, 'message', '', 'filePath', '');
            
            if nargin < 2 || isempty(fileName)
                error('ProjectManagementService:InvalidInput', '文件名不能为空');
            end
            
            % 解析可选参数
            p = inputParser;
            addParameter(p, 'Category', '', @ischar);
            addParameter(p, 'Label', '', @ischar);
            parse(p, varargin{:});
            
            category = p.Results.Category;
            label = p.Results.Label;
            
            try
                fprintf('📄 正在添加文件到项目: %s\n', fileName);
                
                % 调用底层添加函数
                if ~isempty(category) && ~isempty(label)
                    success = addFile2proj(obj.projectObj, fileName, category, label);
                else
                    success = addFile2proj(obj.projectObj, fileName);
                end
                
                if success
                    result.success = true;
                    result.message = sprintf('文件 %s 已成功添加到项目', fileName);
                    result.filePath = fileName;
                    fprintf('✅ 文件添加成功\n');
                else
                    error('添加文件失败');
                end
                
            catch ME
                result.success = false;
                result.message = sprintf('添加文件失败: %s', ME.message);
                result.error = ME;
                
                fprintf('❌ 添加文件失败: %s\n', ME.message);
                
                error('ProjectManagementService:AddFileFailed', result.message);
            end
        end
        
        function result = addFolder(obj, folderPath, varargin)
            % 添加文件夹到项目
            % 输入：
            %   folderPath - 文件夹路径
            %   varargin - 可选参数（Name-Value pairs）
            %     'IncludeChildren' - 是否包含子文件（默认true）
            %     'Category' - 类别名称
            %     'Label' - 标签名称
            % 输出：
            %   result - 包含成功标志和消息的结构体
            
            result = struct('success', false, 'message', '', 'folderPath', '');
            
            if nargin < 2 || isempty(folderPath)
                error('ProjectManagementService:InvalidInput', '文件夹路径不能为空');
            end
            
            % 解析可选参数
            p = inputParser;
            addParameter(p, 'IncludeChildren', true, @islogical);
            addParameter(p, 'Category', '', @ischar);
            addParameter(p, 'Label', '', @ischar);
            parse(p, varargin{:});
            
            includeChildren = p.Results.IncludeChildren;
            category = p.Results.Category;
            label = p.Results.Label;
            
            try
                fprintf('📁 正在添加文件夹到项目: %s\n', folderPath);
                
                % 调用底层添加函数
                if includeChildren
                    if ~isempty(category) && ~isempty(label)
                        success = addFile2proj(obj.projectObj, folderPath, ...
                            'IncludingChildFiles', category, label);
                    else
                        success = addFile2proj(obj.projectObj, folderPath, 'IncludingChildFiles');
                    end
                else
                    if ~isempty(category) && ~isempty(label)
                        success = addFile2proj(obj.projectObj, folderPath, category, label);
                    else
                        success = addFile2proj(obj.projectObj, folderPath);
                    end
                end
                
                if success
                    result.success = true;
                    result.message = sprintf('文件夹 %s 已成功添加到项目', folderPath);
                    result.folderPath = folderPath;
                    fprintf('✅ 文件夹添加成功\n');
                else
                    error('添加文件夹失败');
                end
                
            catch ME
                result.success = false;
                result.message = sprintf('添加文件夹失败: %s', ME.message);
                result.error = ME;
                
                fprintf('❌ 添加文件夹失败: %s\n', ME.message);
                
                error('ProjectManagementService:AddFolderFailed', result.message);
            end
        end
        
        %% ==================== 批量操作 ====================
        
        function results = createMultipleModels(obj, modelNames, varargin)
            % 批量创建模型
            % 输入：
            %   modelNames - 模型名称cell数组
            %   varargin - 传递给createModel的参数
            % 输出：
            %   results - 包含所有创建结果的结构体数组
            % 基于规则第6条：收集所有错误便于调试
            
            if nargin < 2 || isempty(modelNames)
                error('ProjectManagementService:InvalidInput', '模型名称列表不能为空');
            end
            
            if ~iscell(modelNames)
                modelNames = {modelNames};
            end
            
            fprintf('🚀 批量创建模型: %d 个\n', length(modelNames));
            fprintf('═══════════════════════════════════════════\n');
            
            results = cell(length(modelNames), 1);
            successCount = 0;
            
            for i = 1:length(modelNames)
                modelName = modelNames{i};
                fprintf('\n[%d/%d] 创建模型: %s\n', i, length(modelNames), modelName);
                
                try
                    results{i} = obj.createModel(modelName, varargin{:});
                    if results{i}.success
                        successCount = successCount + 1;
                    end
                catch ME
                    results{i} = struct('success', false, ...
                        'message', sprintf('创建失败: %s', ME.message), ...
                        'modelName', modelName, ...
                        'error', ME);
                    fprintf('❌ 失败: %s\n', ME.message);
                end
            end
            
            fprintf('\n═══════════════════════════════════════════\n');
            fprintf('✅ 批量创建完成: %d/%d 成功\n', successCount, length(modelNames));
        end
        
        %% ==================== 验证和检查 ====================
        
        function validateModelName(obj, modelName)
            % 验证模型名称有效性
            % 基于规则第2条：明确报告错误
            
            if isempty(modelName)
                error('ProjectManagementService:InvalidModelName', '模型名称不能为空');
            end
            
            if ~isvarname(modelName)
                error('ProjectManagementService:InvalidModelName', ...
                    '%s 是无效的模型名称。模型名称必须是有效的MATLAB变量名。', modelName);
            end
            
            % 可以添加更多命名规范检查
            % 例如：检查是否符合项目命名约定
        end
        
        function checkModelExists(obj, modelName, shouldExist)
            % 检查模型是否存在
            % 输入：
            %   modelName - 模型名称
            %   shouldExist - true表示应该存在，false表示不应该存在
            
            modelExists = exist(modelName, 'file') ~= 0;
            
            if shouldExist && ~modelExists
                error('ProjectManagementService:ModelNotFound', ...
                    '模型 %s 不存在', modelName);
            end
            
            if ~shouldExist && modelExists
                error('ProjectManagementService:ModelAlreadyExists', ...
                    '模型 %s 已存在', modelName);
            end
        end
        
        function dependencies = checkDependencies(obj, modelName)
            % 检查模型的依赖关系
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   dependencies - 依赖此模型的其他模型列表（cell数组）
            % 注意：这是一个简化实现，实际项目中可能需要更复杂的依赖分析
            
            dependencies = {};
            
            try
                % 获取所有模型
                modelList = getModelList();
                
                % 检查每个模型是否引用了目标模型
                for i = 1:length(modelList)
                    currentModel = modelList{i};
                    if strcmp(currentModel, modelName)
                        continue;
                    end
                    
                    try
                        % 加载模型（不打开）
                        load_system(currentModel);
                        
                        % 查找模型引用块
                        refBlocks = find_system(currentModel, 'BlockType', 'ModelReference');
                        
                        % 检查是否引用了目标模型
                        for j = 1:length(refBlocks)
                            refModel = get_param(refBlocks{j}, 'ModelName');
                            if strcmp(refModel, modelName)
                                dependencies{end+1} = currentModel; %#ok<AGROW>
                                break;
                            end
                        end
                        
                        % 关闭模型
                        bdclose(currentModel);
                    catch
                        % 忽略无法加载的模型
                    end
                end
            catch ME
                warning('ProjectManagementService:DependencyCheckFailed', ...
                    '依赖关系检查失败: %s', ME.message);
            end
        end
        
        %% ==================== 信息查询 ====================
        
        function info = getModelInfo(obj, modelName)
            % 获取模型信息
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   info - 模型信息结构体
            
            info = struct();
            info.name = modelName;
            
            try
                info.exists = exist(modelName, 'file') ~= 0;
                
                if info.exists
                    info.path = ProjectPathManager.getDirPath('model root', modelName);
                    info.modelFile = fullfile(ProjectPathManager.getDirPath('model', modelName), ...
                        [modelName, '.slx']);
                    info.hasDataDictionary = exist(fullfile(...
                        ProjectPathManager.getDirPath('model data', modelName), ...
                        ['DD_', modelName, '.sldd']), 'file') ~= 0;
                    
                    % 检查依赖
                    info.dependencies = obj.checkDependencies(modelName);
                    info.hasDependencies = ~isempty(info.dependencies);
                else
                    info.path = '';
                    info.modelFile = '';
                    info.hasDataDictionary = false;
                    info.dependencies = {};
                    info.hasDependencies = false;
                end
            catch ME
                warning('ProjectManagementService:GetInfoFailed', ...
                    '获取模型信息失败: %s', ME.message);
            end
        end
        
        function listModels(obj)
            % 列出项目中的所有模型
            
            try
                fprintf('📋 项目中的模型列表:\n');
                fprintf('═══════════════════════════════════════════\n');
                
                modelList = getModelList();
                
                if isempty(modelList)
                    fprintf('  (无模型)\n');
                else
                    for i = 1:length(modelList)
                        fprintf('  %d. %s\n', i, modelList{i});
                    end
                end
                
                fprintf('═══════════════════════════════════════════\n');
                fprintf('总计: %d 个模型\n', length(modelList));
            catch ME
                warning('ProjectManagementService:ListModelsFailed', ...
                    '列出模型失败: %s', ME.message);
            end
        end
    end
end

